package com.noahsloan.nutils.swing;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Point;
import java.awt.Toolkit;

import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;

/**
 * SplashScreen's primary use is for a startup splash screen. However, it can
 * also be used for other borderless windows, such as "Loading..." and "Please
 * wait.." hints.
 * 
 * @author noah
 * 
 */
public class SplashScreen extends JWindow {

    private static final long serialVersionUID = -6120477568075390555L;

    /**
     * Creates a splash screen with the given parent, at the given location,
     * from the given contents which will disappear after <code>waitTime</code>ms,
     * unless wait time is null, in which case it will remain until
     * {@link #kill()} is called.
     * <p>
     * <code>contents</code> will be automatically centered in the
     * pseudo-window, however, the pseudo-window is only as big as
     * <code>contents</code>, so if you make contents something like a
     * {@link JPanel}, then you can effectively lay it out however you want.
     * 
     * @param parent
     *            the parent frame.
     * @param contents
     *            the Component to display
     * @param waitTime
     *            how long to display the contents. null means indefinitely.
     * @param location
     *            the screen coordinates of the upper left hand corner.
     */
    public SplashScreen(Frame parent, Component contents,
            final Integer waitTime, Point location) {
        super(parent);
        // display the screen
        getContentPane().add(contents, BorderLayout.CENTER);
        setLocation(location);
        pack();
        setVisible(true);

        // schedule its removal (if a non-null waitTime was given)
        if (waitTime != null && waitTime.intValue() >= 0) {
            final Runnable closerRunner = new Runnable() {
                public void run() {
                    kill();
                }
            };
            Runnable waitRunner = new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(waitTime.intValue());
                        SwingUtilities.invokeAndWait(closerRunner);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            // don't want to use SwingUtilities.invokeLater because the wait
            // will cause the drawing thread to suspend for that long. which is
            // bad
            Thread splashThread = new Thread(waitRunner, "SplashThread");
            splashThread.start();
        }
    }

    /**
     * Create a splash screen with the given contents that disposes after
     * <code>waitTime</code> milliseconds and is centered on the screen.
     * 
     * @param parent
     * @param contents
     * @param waitTime
     *            the number of ms to wait before disposing, or null if the
     *            window should remain indefinitely.
     */
    public SplashScreen(Frame parent, Component contents, final Integer waitTime) {
        this(parent, contents, waitTime, centeredOnScreen(contents));
    }

    /**
     * Convenience constructor for a SplashScreen (centered on the screen) that
     * remains until it is removed by {@link #kill()}.
     * 
     * @param parent
     * @param contents
     */
    public SplashScreen(Frame parent, Component contents) {
        this(parent, contents, null);
    }

    /**
     * Disposes of this Splash Screen
     */
    public void kill() {
        setVisible(false);
        dispose();
    }

    /**
     * 
     * @param component
     *            the component to be centered
     * @param parent
     *            the component to center on
     * @return the point where the top-left corner of the component should go in
     *         order to be centered in the parent component.
     */
    public static Point centeredOnComponent(Component component,
            Component parent) {
        Point tl = parent.getLocation();
        Dimension p = parent.getSize();
        Dimension c = component.getPreferredSize();
        return new Point(tl.x + (p.width - c.width) / 2, tl.y
                + (p.height - c.height) / 2);
    }

    /**
     * 
     * @param component
     *            the component to be centered
     * @return the point where the top-left corner of <code>component</code>
     *         should go to center this window on the screen.
     */
    public static Point centeredOnScreen(Component component) {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension labelSize = component.getPreferredSize();
        return new Point((screenSize.width - labelSize.width) / 2,
                (screenSize.height - labelSize.height) / 2);
    }

}